#include "frame_delayer.hh"

namespace kratos { namespace engine {

FrameDelayer::FrameDelayer(int frame)
    : feedback_(0), begin_(0), end_(0), overwork_(0) {
    if (frame > 1000) {
        frame = 1000; // maximum frame frequency
    }
    // Calculates the gap between frame
    if (!frame) {
        frameInMillions_ = MILLION_RESOLUTION / DEFAULT_FRAME;
    }  else {
        frameInMillions_ = MILLION_RESOLUTION / frame;
    }
}

FrameDelayer::~FrameDelayer() {
    frameInMillions_ = 0;
    feedback_ = 0;
    begin_ = 0;
    end_ = 0;
    overwork_ = 0;
}

void FrameDelayer::beginFrame(std::time_t now) {
    if (!now) {
        begin_ = corelib::TimeUtil::getMillionSecondsTimestampOS();
    } else {
        begin_ = now;
    }
}

void FrameDelayer::endFrame(std::time_t now) {
    if (!now) {
        end_ = corelib::TimeUtil::getMillionSecondsTimestampOS();
    } else {
        end_ = now;
    }
    feedback_ = end_ - begin_;
    tryDelay();
}

void FrameDelayer::tryDelay() {
    if (overwork_ >= OVERWORK_THRESHOLD) {
        // try to yield cpu core
        std::this_thread::yield();
        return;
    }
    if (feedback_ >= frameInMillions_) {
        overwork_ += 1;
        return;
    } else {
        uint64_t delta = frameInMillions_ - feedback_;
        if (overwork_ > 0) {
            overwork_ -= 1;
        }
        if (delta < CPU_TIME_SLICE) {
            delta = 1;
        }
        if (delta > DELTA_MAX) {
            delta = DELTA_MAX;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(delta));
    }
}

}}
